/**
 * \file debug.cpp
 * \brief Internal debugger
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl>
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#include <stdio.h>
#include <signal.h>
#include <aterm1.h>

#include "Code.h"
#include "Symbol.h"
#include "parse.h"

extern "C" char *readline(const char *prompt);
extern "C" void add_history (const char *string);

bool broken = false;
bool interactive = false;

std::string readCommand()
{
  char *buf = readline(">> ");
  std::string str = std::string(buf);
  
  if (buf)
    add_history(buf);

  return str;
}

void debugBreakHandler(int signum)
{
  if (broken)
    exit(1);
  else
    broken = true;
}

void debugInit(void)
{
  signal(SIGINT, debugBreakHandler);
}

void debugHook(Code *code, SymbolTable *symbols, Compound *out)
{
  typedef std::list<std::string>::iterator LI;

  if (broken)
  {
    std::cout << code->toString();
    std::string cmd;

    do
    {
      cmd = readCommand();

      if (cmd == "q")
      {
	exit(0);
      }
      else if (cmd == "c")
      {
	broken = false;
      }
      else if (cmd == "b")
      {
	if (!options.breakPoints.empty())
	{
	  std::cout << "Breakpoints at call of" << std::endl;
	  for (LI ii=options.breakPoints.begin();
	       ii != options.breakPoints.end(); ++ii)
	    std::cout << "  " << (*ii) << std::endl;
	}
	else 
	  std::cout << "No breakpoints defined" << std::endl;
      }
      else if (!cmd.compare(0, 2, "b "))
      {
	options.breakPoints.push_back(&cmd.c_str()[2]);
	std::cout << "Added breakpoint at call of " << &cmd.c_str()[2] << std::endl;
      }
      else if (cmd == "s")
      {
	std::cout << symbols->print();
      }
      else if (!cmd.compare(0, 2, "s "))
      {
	std::cout << symbols->symbolToString(&cmd.c_str()[2]) << std::endl;
      }
      else if (cmd == "p")
      {
	std::cout << code->toString();
      }
      else if (cmd == "h")
      {
	std::cout << "PEPCI debugger help" << std::endl;
	std::cout << "  p       print current statement" << std::endl;
	std::cout << "  n       proceed to next statement" << std::endl;
	std::cout << "  c       stop debugging and continue executing" << std::endl;
	std::cout << "  b [SYM] add breakpoint at call of SYM" << std::endl;
	std::cout << "  s [SYM] print SYM, or the whole table" << std::endl;
	std::cout << "  q       stop debugging and exit" << std::endl;
	std::cout << "  h       print this help" << std::endl;
	std::cout << "  <STAT>  execute statement STAT" << std::endl;
      }
      else if (cmd != "" && cmd != "n") 
      {
	/* Parse command */
	Code *cmdcode = parseC(cmd.c_str());
	Stat *stat = dynamic_cast<Stat*>(cmdcode);
	
	if (stat)
	{
	  interactive = true;
	  delete stat->reduce(symbols, out);
	  interactive = false;
	}
	else
        {
	  std::cout << "Invalid command. Use 'h' for help" << std::endl;
        }
      }
    } while (broken && cmd != "n" && cmd != "");
  }
}
